﻿
Imports System.Net.Sockets
Imports System.Threading

Public Class tcpServer3
    Public Event receive(ByVal handler As talkHandler)
    Public Const defaultHoldTimeMs As Int64 = 30000
    Public Const defaultLiveTimeMs As Int64 = 300000
    Public Const defaultMaxConnectionCount As Int64 = 128
    Private Shared ReadOnly TCPSERVER3_REQUEST_QPS As Int64
    Private Shared ReadOnly TCPSERVER3_INTERNAL_TIMEMS As Int64
    Private Shared ReadOnly TCPSERVER3_RESPONSE_TIMEMS As Int64
    Public holdTimeMs As Int64 = defaultHoldTimeMs
    Public liveTimeMs As Int64 = defaultLiveTimeMs
    Public maxConnectionCount As Int64 = defaultMaxConnectionCount
    Public legacyDataSize As Boolean = False
    Private listener As tcpListener2 = Nothing

    Private _stopping As Boolean = False
    Public Property stopping As Boolean
        Get
            Return _stopping
        End Get
        Private Set(ByVal value As Boolean)
            _stopping = value
        End Set
    End Property

    Private _port As Int32 = npos
    Public Property port As Int32
        Get
            Return _port
        End Get
        Set(ByVal value As Int32)
            If _port <> value Then
                _port = value
                If active() Then
                    reset()
                End If
            End If
        End Set
    End Property

    Private _connectionCount As Int32 = 0
    Public Function connectionCount() As Int32
        Return _connectionCount
    End Function

    Private Sub incrementConnectionCount()
        Interlocked.Increment(_connectionCount)
    End Sub

    Protected Sub decrementConnectionCount()
        Interlocked.Decrement(_connectionCount)
    End Sub

    Public Function active() As Boolean
        Return Not listener Is Nothing AndAlso listener.Active()
    End Function

    Private Sub closeListener()
        If Not listener Is Nothing Then
            socketTrans.close(listener.Server)
            listener.Stop()
            listener = Nothing
        End If
        GC.KeepAlive(listener)
    End Sub

    Public Sub [stop](Optional ByVal waitTime As Int64 = 10)
        If active() Then
            stopping = True
            closeListener()
            While connectionCount() > 0 AndAlso waitTime > 0
                sleepSeconds(1)
                waitTime -= 1
            End While
        End If
    End Sub

    Public Sub reset()
        [stop]()
        start()
    End Sub

    Public Function start(ByVal port As Int32) As Boolean
        Me.port = port
        Return start()
    End Function

    Protected Overridable Sub createHandler(ByVal client As TcpClient)
        Dim handler As talkHandler = Nothing
        handler = New talkHandler(Me, client)
        Dim action As callbackAction = Nothing
        Dim receiveAction As callbackAction = Nothing
        action = New callbackAction(check:=callbackAction.rotate_check(
                                               Function() As ternary
                                                   If handler.overblanktime() Then
                                                       Return callbackAction.step_check_finish
                                                   ElseIf Not handler.finished() Then
                                                       Return callbackAction.step_check_stay
                                                   ElseIf handler.receive(receiveAction) Then
                                                       Return callbackAction.step_check_pass
                                                   Else
                                                       Return callbackAction.step_check_finish
                                                   End If
                                               End Function,
                                               callbackAction.action_sub_check(
                                                   Function() As callbackAction
                                                       Return receiveAction
                                                   End Function,
                                                   Sub()
                                                       counter.increase(TCPSERVER3_REQUEST_QPS)
                                                       RaiseEvent receive(handler)
                                                   End Sub)),
                                    [end]:=Function() As Boolean
                                               handler.close()
                                               decrementConnectionCount()
                                               Return True
                                           End Function)
        assert_begin(action)
    End Sub

    Private Sub listen(Optional ByRef action As callbackAction = Nothing)
        action = New callbackAction(check:=Function() As Boolean
                                               If stopping() OrElse Not active() Then
                                                   Return True
                                               Else
                                                   While listener.Pending() AndAlso
                                                         connectionCount() < maxConnectionCount
                                                       incrementConnectionCount()
                                                       createHandler(listener.AcceptTcpClient())
                                                   End While
                                                   Return False
                                               End If
                                           End Function)
        assert_begin(action)
    End Sub

    Public Function start() As Boolean
        If Not active() Then
            stopping = False
            closeListener()
            listener = New tcpListener2(port)
            socketTrans.initial(listener.Server)
            listener.Server.SendTimeout = holdTimeMs
            listener.Server.ReceiveTimeout = holdTimeMs
            Try
                listener.Start()
            Catch ex As Exception
                raiseError(errorHandle.errorType.warning, "failed to start listener, ex ", ex.Message)
                Return False
            End Try
            listen()
            Return active()
        Else
            Return False
        End If
    End Function

    Shared Sub New()
        TCPSERVER3_REQUEST_QPS = counter.registerRateAndLastRate("TCPSERVER3_REQUEST_QPS")
        TCPSERVER3_RESPONSE_TIMEMS = counter.registerAverageAndLastAverage("TCPSERVER3_RESPONSE_TIMEMS")
        TCPSERVER3_INTERNAL_TIMEMS = counter.registerAverageAndLastAverage("TCPSERVER3_INTERNAL_TIMEMS")
    End Sub

    Protected Overrides Sub Finalize()
        closeListener()
        MyBase.Finalize()
    End Sub

    Public Class talkHandler
        Protected server As tcpServer3
        Protected client As TcpClient
        Protected lastReceiveMs As Int64
        Protected _receive As nettransData
        Private _finished As Boolean

        Private Sub initial()
            lastReceiveMs = Now.milliseconds()
            _finished = True
            tcpTrans.initialTcpClient(client)
        End Sub

        Private Sub assert_me()
            assert(Not server Is Nothing)
            assert(Not client Is Nothing)
            assert(finished())
            assert(Not overblanktime())
        End Sub

        Friend Sub New(ByVal server As tcpServer3, ByVal client As TcpClient)
            Me.server = server
            Me.client = client
            initial()
            assert_me()
        End Sub

        Public Function receiveData() As nettransData
            Return _receive
        End Function

        Private Function timeoutticks() As Int64
            Return millisecondsToTicks(server.holdTimeMs)
        End Function

        Public Function [return](ByVal data As nettransData,
                                 Optional ByRef action As callbackAction = Nothing) As Boolean
            Dim sendAction As callbackAction = Nothing
            counter.recordTimeMs(TCPSERVER3_INTERNAL_TIMEMS, lastReceiveMs)
            action = New callbackAction(Function() As Boolean
                                            Return tcpTrans3.send(client, data,
                                                                  server.holdTimeMs,
                                                                  sendAction,
                                                                  server.legacyDataSize)
                                        End Function,
                                        callbackAction.action_check(Function() As callbackAction
                                                                        Return sendAction
                                                                    End Function),
                                        Function() As Boolean
                                            _finished = True
                                            If callbackAction.success_finished(sendAction) Then
                                                counter.recordTimeMs(TCPSERVER3_RESPONSE_TIMEMS, lastReceiveMs)
                                                Return True
                                            Else
                                                Return False
                                            End If
                                        End Function,
                                        timeoutticks())
            Return begin(action)
        End Function

        Friend Function finished() As Boolean
            Return _finished
        End Function

        Friend Function overblanktime() As Boolean
            Return Now.milliseconds() - lastReceiveMs >= server.liveTimeMs OrElse
                   server.stopping()
        End Function

        Public Function closed() As Boolean
            Return tcpTrans.clientClosed(client)
        End Function

        Friend Sub close()
            tcpTrans.closeTcpClient(client)
        End Sub

        Protected Function afterReceive() As Boolean
            Return eva(lastReceiveMs, Now.milliseconds()) AndAlso eva(_finished, False)
        End Function

        Friend Function receive(ByRef action As callbackAction) As Boolean
            assert(finished())
            Dim receiveAction As callbackAction = Nothing
            action = New callbackAction(Function() As Boolean
                                            Return tcpTrans3.receive(client,
                                                                     _receive,
                                                                     server.holdTimeMs,
                                                                     action:=receiveAction,
                                                                     legacyDataSize:=server.legacyDataSize)
                                        End Function,
                                        callbackAction.action_check(Function() As callbackAction
                                                                        Return receiveAction
                                                                    End Function),
                                        Function() As Boolean
                                            Return callbackAction.success_finished(receiveAction) AndAlso
                                                   afterReceive()
                                        End Function,
                                        timeoutticks())
            Return begin(action)
        End Function

        Public Overrides Function ToString() As String
            If client Is Nothing OrElse
               client.Client() Is Nothing OrElse
               client.Client().RemoteEndPoint() Is Nothing Then
                Return "#CANNOT_TRACE_REMOTEENDPOINT#"
            Else
                Return Convert.ToString(client.Client().RemoteEndPoint())
            End If
        End Function
    End Class
End Class
